#encoding=utf-8
'''
a sampile tcpserver framework use epoll 
'''

import socket
import threading
import functools
from  epollLoop import EPollLoop as EPollLoop 

_DBUG = True

def DEGUG(strings):
	if globals()['_DBUG']:
		print strings

class TcpServer(object):

	def __init__(self, port, ip='0.0.0.0', max_listen = 128):
		self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		#set the port can be reuse by the socket
		self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
		self._socket.bind((ip, port))
		self._socket.listen(max_listen)
		#set the socket no blocking
		self._socket.setblocking(False)
		self._epoll_loop = EPollLoop.instance()
		EPollLoop._accept_socket = self._socket


	@staticmethod
	def default_accpet_handler(sockets, events, handler):

		def _accept_handler(srv_socket, srv_events):
			if sockets.fileno() != srv_socket.fileno():
				raise BaseException,'accept hanler is err'

 			if srv_events&EPollLoop._EVENT_RD:
 				# when this event is RD  a new client connect this server
 				#we should handler the client 
 				try:
					connection, address = srv_socket.accept()
				except :
					return
				DEGUG(address)
				connection.setblocking(False)
				#向epoll池中注册这个新连接的客户端
				EPollLoop.instance().register_handler(connection, events, StreamProcess(handler))
			else:
				return
		EPollLoop.instance().register_handler(sockets, EPollLoop._EVENT_RD, _accept_handler)


	def set_callback(self, events, callback):
		'''
		if not isinstance(callback, StreamProcess):
			return
		'''
		TcpServer.default_accpet_handler(self._socket, events, callback)

class IoBuffer(object):
	def __init__(self):
		self._r_buf = ''
		self._w_buf = ''

	def get_read_buf(self, size):
		if size >= len(self._r_buf):
			t = self._r_buf
			self._r_buf = ''
			return t
		else:
			t = self._r_buf[0:size]
			self._r_buf = self._r_buf[size:]
			return t
	def get_read_buf_all(self):
		t = self._r_buf
		self._r_buf = ''
		return t

	def append_to_read_buf(self, contant):
		self._r_buf='%s%s'%(self._r_buf,contant)

	def append_to_write_buf(self, contant):
		self._w_buf='%s%s'%(self._w_buf,contant)

	def get_write_buf_all(self):
		t = self._w_buf
		self._w_buf = ''
		return t

	def get_write_buf(self, size):
		if size >= len(self._w_buf):
			t = self._w_buf
			self._w_buf = ''
			return t
		else:
			t = self._w_buf[0:size]
			self._w_buf = self._w_buf[size:]
			return t


class StreamProcess(object):
	'''
	一个用于处理接收和发送tcp数据流的类，当epoll池中出现任何事件(监听套接字除外)
	最终都会调用这个类来进行处理 上层只需要提取接收的数据或者将数据提交给它即可
	'''
	_MAX_RECV_LEN = 10240

	def __new__(cls, *args, **kw):
		#实现设计模式中的工厂方法
		return super(StreamProcess, cls).__new__(args[0])

	def __init__(self, *args, **kw):
		#self._callback = callback
		self._io_buf = IoBuffer()
		self._is_ansy = False

	def __call__(self, sockets, events):
		self._sockets = sockets
		self._events = events
		#
		if not self._is_ansy:
			self.handler(self._io_buf)
			self._is_ansy = True
		#如果是可读事件，将数据内容给读取出来 然后将内容传给顶层应用去处理
		if events&EPollLoop._EVENT_RD:
			contant = sockets.recv(StreamProcess._MAX_RECV_LEN)
			if contant == '':
				#说明是客户端断开了连接或者网络出现了异常 引发一个异常 让EPollLoop去注销这个客户端
				self._io_buf = None
				raise BaseException()
			else:
				self._io_buf.append_to_read_buf(contant)
				#self.handler(self._io_buf)
				#self._callback(sockets, contant)
				return 
		elif events&EPollLoop._EVENT_WR:
			#
			self._handler_send()
			return
		else:
			#err raise exception
			raise BaseException()
	
	#overwrite this function
	def handler(self, io_buffer):
		raise Exception,'must realise handler function'

	def finish(self):
		self._sockets.sendall(self._io_buf.get_write_buf())

	def start_send(self):
		#向epoll中注册可写事件 epoll去调用该handler处理IoBuffer中的写缓冲区
		#如果可以发送数据 会调用当前对象的__call__
		if self._io_buf:
			EPollLoop.instance().update_fd_event(self._sockets, EPollLoop._EVENT_RD|EPollLoop._EVENT_WR)
		else:
			#当IoBuf被释放后，应该引发一个异常 让处理这个IOBUF的线程退出
			raise BaseException()

	def _handler_send(self):
		contant = self._io_buf.get_write_buf(StreamProcess._MAX_RECV_LEN)
		if len(contant) == 0:
			#说明没有数据要发送了 注销写事件
			#EPollLoop.instance().update_fd_event(self._sockets, (self._events&(~EPollLoop._EVENT_WR)))
			EPollLoop.instance().update_fd_event(self._sockets, EPollLoop._EVENT_RD)
		while contant:
			ret = self._sockets.send(contant)
			if ret < len(contant):
				contant = contant[ret:]
				continue
			else:
				contant = None

	@staticmethod
	def asynchronous_thread(func):
		'''
		为什么要异步?
		这个很明显，当我们的handler函数处理时间过长，后面的事件就执行不了了,一直在等待
		如何异步?
		这个我也在考虑这个问题,目前暂且使用多线程的方式吧  我觉得应该有更好的方法去实现异步功能以后有时间再去考虑
		'''
		def thread_main(*args, **kw):
			try:
				func(args[0], args[1])
			except:
				#exit thread
				return
		@functools.wraps(func)
		def wrapper(self, io_buf):
			tid = threading.Thread(target=thread_main, args=(self, self._io_buf,))
			tid.setDaemon(True)
			tid.start()
		return wrapper




#test
import time
class demo(StreamProcess):

	@StreamProcess.asynchronous_thread
	def handler(self, io_buffer):
		while True:
			time.sleep(10)
			c = self._io_buf.get_read_buf_all()
			self._io_buf.append_to_write_buf(c)
			self.start_send()

if __name__ == "__main__":
	server = TcpServer(8080)
	server.set_callback(EPollLoop._EVENT_RD, demo)
	EPollLoop.instance().start()




